package sk.ygor.raynor.rebind.serializable

import com.google.gwt.core.ext.typeinfo.{JClassType, JType}
import sk.ygor.raynor.client.reflection.JSONTypeInfo

object CollectionTypeAnalysis extends TypeAnalysis {
  def runAnalysis(serializableType: JType): AnalysisResult = {
    val chain = List(isAllowedListType _, isAllowedMapType _, isAllowedArrayType _)
    var rejections = List[String]()
    for (f <- chain) {
      f(serializableType) match {
        case x@Analyzed(structureInfo) => return x
        case Rejected(msg) => rejections = msg :: rejections
      }
    }
    Rejected(rejections.mkString(" +++ "))
  }

  def isAllowedMapType(jType: JType): AnalysisResult = {
    if (!(jType equalToJavaClass classOf[java.util.Map[_, _]])) {
      Rejected("Not a java.util.Map")
    } else {
      lazy val rejectWrongMap = Rejected("Only parametrized maps Map<String, ?> are allowed, found: " + jType.getParameterizedQualifiedSourceName)
      jType.maybeParameterized match {
        case Some(jParameterizedType) =>
          if (!jParameterizedType.getTypeArgs()(0).getQualifiedSourceName().equals(classOf[String].getName()))
            rejectWrongMap
          else
            Analyzed(CollectionTypeStructureInfo(JSONTypeInfo.CollectionType.Nature.STRING_MAP, jParameterizedType.getTypeArgs()(1)))
        case None =>
          rejectWrongMap
      }
    }
  }

  def isAllowedListType(jType: JType): AnalysisResult = {
    if (jType.getQualifiedSourceName() != classOf[java.util.List[_]].getName()) {
      Rejected("Not a java.util.List")
    } else {
      jType.maybeParameterized match {
        case Some(jParameterizedType) =>
          Analyzed(CollectionTypeStructureInfo(JSONTypeInfo.CollectionType.Nature.LIST, jParameterizedType.getTypeArgs()(0)))
        case None =>
          Rejected("Only parametrized lists are allowed. Found: " + jType.getParameterizedQualifiedSourceName())

      }
    }
  }

  def isAllowedArrayType(jType: JType): AnalysisResult = {
    jType.maybeArray match {
      case None =>
        Rejected("Not an array")
      case Some(arrayType) =>
        val tail = arrayType.getComponentType();
        tail.maybeClassOrInterface match {
          case Some(classOrInterfaceTail) =>
            Analyzed(CollectionTypeStructureInfo(JSONTypeInfo.CollectionType.Nature.ARRAY, classOrInterfaceTail))
          case None => tail.maybeArray match {
            case Some(arrayTail) =>
              Analyzed(CollectionTypeStructureInfo(JSONTypeInfo.CollectionType.Nature.ARRAY, arrayTail))
            case None =>
              Rejected("It is an array, but the component is not a class type: " + tail.getParameterizedQualifiedSourceName)
          }
        }

    }
  }

}


case class CollectionTypeStructureInfo(nature: JSONTypeInfo.CollectionType.Nature,
                                       tail: JClassType) extends StructureInfo {
  def prefix = nature.name

  def serializer = ClassInfo(
    JSONSerializableAnalysis.getStructureInfo(tail).serializer.packageName,
    //    classOf[JSONSerializer[_]].getPackage().getName(),
    prefix + "_" + normalizedTailTypeName + "_JSONSerializer"
    )

  def normalizedTailTypeName = tail.getParameterizedQualifiedSourceName().replaceAll("[<>\\.]", "_")

  def dependendTypes = List(tail)

}


